import { Project,Node } from "ts-morph";
import * as fs from 'fs';

/**
 * ast节点类，属性包括父节点，子节点列表，种类，文本内容，开始位置
 */
export class NodeA{
    children:NodeA[];
    kind:string;
    text:string;
    start:number;
    constructor(node:Node|undefined,children:NodeA[]){
        this.children=children;
        if(node==undefined){
            this.kind="undefined";
            this.text="undefined";
            this.start=0;
        }
        else{
            this.kind=node.getKindName();
            this.text=node.getText();
            this.start=node.getStart();
        }
        
    }
}

/**
 * ast类，目前的构造方式是传ts代码，之后可以考虑传文件路径等
 */
export class ASTree{
    text:string;
    root:NodeA=new NodeA(undefined,[]);
    constructor(text:string){
        this.text=text;
        this.buildTree();
    }

    /**
     * 复制typescript的ast，因为typescript的ast的节点不能直接操作，因此通过之前自己建立的节点类进行复制
     * @param nodea 复制到nodea
     * @param node 要复制的节点node
     * @returns 
     */
    copyTree(nodea:NodeA,node:Node|undefined) {
        let children=node?.getChildren();
        if (children==null) {
            return;
        }
        let cas:NodeA[]=[];
        for(let child of children){
            let ca=new NodeA(child,[])
            this.copyTree(ca,child)
            cas.push(ca)
        }
        nodea.children=cas
    }

    // 建树
    buildTree(){
        const project=new Project();
        // project.addSourceFilesAtPaths(this.file);
        project.createSourceFile("./file.ts",this.text)
        const sourceFiles = project.getSourceFiles();

        const sourceflie=sourceFiles[0]
        const rootN=sourceflie.getFirstChild()
        if (rootN==null)
            process.exit(0);
        const rootA=new NodeA(rootN,[])
        this.root=rootA
        this.copyTree(rootA,rootN)
    }

    transform(){
        function replaceSubstringAtIndex(inputString: string, index: number, newSubstring: string): string {
            if (index < 0 || index >= inputString.length) {
              // 如果索引越界，则返回原始字符串
              return inputString;
            }
          
            // 将字符串分成两部分，用新子字符串替换指定位置的子字符串
            const before = inputString.slice(0, index); // 截取指定位置之前的部分
            const after = inputString.slice(index + newSubstring.length); // 截取指定位置之后的部分
          
            // 拼接前部分、新子字符串和后部分
            const resultString = before + newSubstring + after;
          
            return resultString;
          }
        
        
        
        let root=this.root;
        
        /**
         * struct关键字处理
         */
        
        let structStart:number[]=[];
        function checkStruct(node:NodeA){
            if(node.text=="struct" && node.kind=="Identifier" ){
                structStart.push(node.start)
            }
            if(node.children==null)
                return;
            for(let c of node.children){
                checkStruct(c);
            }
        }
        
        checkStruct(root);
        let replaceText=this.text;
        for(let i of structStart){
            replaceText=replaceSubstringAtIndex(replaceText,i,'class ');
        }
        let ast=new ASTree(replaceText);
        root=ast.root;
        
        function replaceStruct(node:NodeA){
            if(structStart.includes(node.start)){
                replaceParent(node,node.start,"struct","Class","Struct")
            }
            if(node.children==null)
                return;
            for(let c of node.children)
                replaceStruct(c);
            
        }
        
        function replaceParent(node:NodeA|null,index:number,replaceStr:string,kindOrigin:string,kindReplace:string){
            let p=node;
            let kindReplaced=false;
            while (p!=null) {
                p.text=replaceSubstringAtIndex(p.text,index-p.start,replaceStr);
                if (!kindReplaced)
                    p.kind=p.kind.replace(kindOrigin, kindReplace);
                if(p.kind=="StructDeclaration")
                    kindReplaced=true;
            }
        }
        
        replaceStruct(root)
        
        /**
         * 识别Resource关键字
         */
        
        function checkResource(node:NodeA){
            if(node.kind=="TypeReference" && node.text=="Resource"){
                node.kind="ResourceKeyword";
                node.children=[];
            }
            if(node.children==null)
                return;
            for(let c of node.children){
                checkResource(c);
            }
        }
        checkResource(root)
        
        function checkBuild(node:NodeA){
            if(node.kind=="StructDeclaration"){
                for(let child of node.children){
                    if(child.kind=="SyntaxList"){
                        for(let declaration of child.children){
                            if(declaration.kind=="MethodDeclaration"&&declaration.children[0].text=="build"){
                                declaration.kind="Build";
                                declaration.children[0].kind="BuildKeyword"
                            }
                        }
                    }
                }
            }
            if(node.children==null)
                return;
            for(let c of node.children){
                checkBuild(c);
            }
        }
        checkBuild(root)
        this.root=root;
        // ast.printAST();
    }


    singlePrintAST(node:NodeA,i:number){
        console.log(' '.repeat(i)+node.kind)
        if (node.children==null)
            return;
        for(let c of node.children){
            this.singlePrintAST(c,i+1)
        }
    }

    printAST(){
        if(this.root==null){
            console.log("no root")
        }
        this.singlePrintAST(this.root,0)
    }
}

